/*
 * imx6 接口模拟imx8的mxs-adc，主要修改在ioctl，节点名称 mxs-adc
 * tk100-adc {
 *     compatible = "tk100-imx6-2-mxs-adc";
 * };
 *
 * insmod  tk100-imx6-adc.ko  imx6_adc=4000
 */

#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>

#include <linux/miscdevice.h>

#include <linux/gpio.h>
#include <linux/gpio/consumer.h>

#include <linux/of_gpio.h>

#include <linux/interrupt.h>
#include <linux/delay.h>

#include <linux/workqueue.h>

#include <linux/fs.h>


#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
// #include <linux/sysdev.h>
#include <linux/bitops.h>
#include <linux/sysfs.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>

#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/fs.h>



#include <linux/module.h>	 
#include <linux/fs.h>		 
#include <linux/uaccess.h>	 
#include <linux/miscdevice.h>
#include <asm/io.h>			 
// #include <mach/regs-lradc.h> 

#define HSADC_DEVICE_NAME "mxs-adc" /* hsadc device name */

#ifdef ACTIVE_LOW
#define KS_PRESS(n) (!(n))
#else
#define KS_PRESS(n) (!!(n))
#endif

struct pwr_btn_data {
	unsigned int gpio_key;
	unsigned int gpio_keep;
	char *backlight_path;

	int active;
	int jiffies_start;
	int times;

	struct timer_list timer;
	struct work_struct work_bl;
	struct work_struct work_key;
};

struct mxs_hsadc_data
{
	/* char device and file operations interface */
	struct cdev cdev_inst;    /* cdev instance */
	struct class *cdev_class; /* hsadc device class for sysfs */
	int cdev_major;           /* hsadc major number */
	int cdev_index;           /* hsadc index number */
};




static const struct of_device_id of_pwr_btn_match[] = {
	{ .compatible = "tk100-imx6-2-mxs-adc", },
	{},
};
MODULE_DEVICE_TABLE(of, of_pwr_btn_match);



ssize_t hsadc_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
	return 0;
}
// int imx6_adc = 0;
static int imx6_adc = 0;
module_param(imx6_adc, int, S_IRUGO);
static int dbg = 0;
module_param(dbg, int, S_IRUGO);

// static void __attribute__((constructor)) _load_env()
// {
// 	char *env;
// 	env = getenv("IMX6_ADC");
// 	if (env) {
// 		imx6_adc = atoi(env);
// 	}
// }

/*
 26 imx287 ADC6 功率采集 对应imx6 adc5
 21 imx287 ADC6 电池采集 对应imx6 adc8
 20 imx287 ADC6 电池采集 对应imx6 adc8
*/ 
static long adc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct file *fp;
    loff_t offset = 0;
    char *str_file;
    char buf[4];
    int dec;

    if (dbg) {
		printk("cmd %d arg %x\r\n", cmd , arg);
	}

    switch(cmd) {
        case 1:  // 仅仅为了调试
        case 16:
		case 26:
            str_file = "/sys/bus/iio/devices/iio\:device0/in_voltage5_raw";
            break;
        case 21:
        case 11:
        case 20:
        case 10: //两颗点此看成一颗
            str_file = "/sys/bus/iio/devices/iio\:device0/in_voltage8_raw";
            break;
        
        default:
            printk("%s() unknow adc cmd", __FUNCTION__);
            break;
    }



    fp = filp_open(str_file, O_RDWR, 0);
    if (fp < 0) {
        return 0;
    }
    

    kernel_read(fp, buf, sizeof(int),  &offset); 
    dec = simple_strtol(buf, NULL, 10);
	
	if (imx6_adc) {
		dec = imx6_adc;
	}
    copy_to_user((void *)arg, (void *)(&dec), sizeof(int));
	filp_close(fp, 0);
    return 0;   
}
static int adc_open(struct inode *inode, struct file *fp)
{
    return 0;
}
static int adc_release(struct inode *inode, struct file *fp)
{
	return 0;
}
// static const struct file_operations hsadc_fops = {
// 	.owner = THIS_MODULE,
// 	// .read = hsadc_read,
// 	.unlocked_ioctl = adc_ioctl,
//     .compat_ioctl = adc_ioctl,
// 	.open = adc_open,
// 	.release = adc_release,
// };
// void hsadc_cleanup_cdev(struct mxs_hsadc_data *pdx);
/* mxs-hsadc init driver interface */
// int hsadc_init_cdev(struct mxs_hsadc_data *pdx)
// {
// 	int res = 0;
// 	dev_t devno = 0;

// 	/* allocate chrdev region dynamical */
// 	res = alloc_chrdev_region(&devno, 0, 1, HSADC_DEVICE_NAME);

// 	if (res < 0)
// 		return res;

// 	/* init the semaphore as a mutex */
// 	// sema_init(&pdx->sem, 1); /* @== init_MUTEX(&gdevp->sem) */

// 	// init_waitqueue_head(&pdx->r_wait);

// 	pdx->cdev_major = MAJOR(devno);
// 	pdx->cdev_index = 0;
// 	pdx->cdev_inst.owner = THIS_MODULE;

// 	/* populate sysfs device entry(diractory) */
// 	pdx->cdev_class = class_create(THIS_MODULE, HSADC_DEVICE_NAME);

// 	/* init the cdev kernel object, set the fops for user space interface */
// 	cdev_init(&pdx->cdev_inst, &hsadc_fops);

// 	res = cdev_add(&pdx->cdev_inst, MKDEV(pdx->cdev_major, pdx->cdev_index), 1);
// 	if (res)
// 	{
// #if HSADC_DEBUG
// 		printk(KERN_NOTICE "%s> error %d adding hsadc-%d.\n", HSADC_DEVICE_NAME, res, 0);
// #endif
// 		goto fail_cdev_add;
// 	}

// 	/* send uevents to udev，the udevd dynamic create device node(inode) in /dev */
// 	device_create(pdx->cdev_class, NULL, MKDEV(pdx->cdev_major, pdx->cdev_index), NULL, "%s%d", HSADC_DEVICE_NAME, 0);

// #if HSADC_DEBUG
// 	printk(KERN_INFO "%s> initialized.\n", HSADC_DEVICE_NAME);
// #endif

// 	return 0;

// fail_cdev_add:
// 	class_destroy(pdx->cdev_class);

// 	//fail_malloc:
// 	unregister_chrdev_region(devno, 1);
// 	return res;
// }

// void hsadc_cleanup_cdev(struct mxs_hsadc_data *pdx)
// {
// 	device_destroy(pdx->cdev_class, MKDEV(pdx->cdev_major, pdx->cdev_index));
// 	cdev_del(&pdx->cdev_inst); /* against to cdev_add() */
// 	class_destroy(pdx->cdev_class);
// 	unregister_chrdev_region(MKDEV(pdx->cdev_major, pdx->cdev_index), 1); /* release the region */

// #if HSADC_DEBUG
// 	printk(KERN_INFO "%s> %s.\n", HSADC_DEVICE_NAME, __FUNCTION__);
// #endif

// 	return;
// }

struct file_operations adc_fops =
	{
		.owner = THIS_MODULE,
        .unlocked_ioctl = adc_ioctl,
        .compat_ioctl = adc_ioctl,
		.open = adc_open,
		.release = adc_release,
		// .ioctl = adc_ioctl,
};

static struct miscdevice adc_miscdev =
	{
		.minor = MISC_DYNAMIC_MINOR,
		.name = "mxs-adc",
		.fops = &adc_fops,
};
static int  mxs_hsadc_probe(struct platform_device *pdev)
	// static int __devinit mxs_hsadc_probe(struct platform_device *pdev)
{
    printk("----------\r\n");
	struct mxs_hsadc_data *pd;
	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
    platform_set_drvdata(pdev, pd);
	// hsadc_init_cdev(pd);
    int ret = misc_register(&adc_miscdev);
}

static int  mxs_hsadc_remove(struct platform_device *pdev)
{
	struct mxs_hsadc_data *pd = platform_get_drvdata(pdev);
	// hsadc_cleanup_cdev(pd); // clean cdev interface
}
static struct platform_driver tk100_imx6_adc_driver = {
	.probe		= mxs_hsadc_probe,
	.remove = __exit_p(mxs_hsadc_remove),
	.driver		= {
		.name	= "mxs-adc",
		.of_match_table = of_pwr_btn_match,
	},
};

static int __init tk100_imx6_adc_init(void)
{

	return platform_driver_register(&tk100_imx6_adc_driver);
}

static void __exit tk100_imx6_adc_exit(void)
{
	// printk("exit\r\n");
	platform_driver_unregister(&tk100_imx6_adc_driver);
    misc_deregister(&adc_miscdev);
}






module_init(tk100_imx6_adc_init);
module_exit(tk100_imx6_adc_exit);


MODULE_AUTHOR("MengLong Wu <MenglongWu@aliyun.com>");
MODULE_DESCRIPTION("keep power on/off,power key");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:power-btn");
